﻿[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Items.MItem.js", "application/x-javascript")]
[assembly: System.Web.UI.WebResource("BM.Tools.WebControls.MForm.Controls.Items.MItem.debug.js", "application/x-javascript")]

namespace BM.Tools.WebControls.MForm.Controls.Items
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Web.UI;
    using System.Web.UI.WebControls;    
    using System.Xml.XPath;

    using AjaxControlToolkit;

    using BM.Tools.WebControls.MForm.Controls.Additions;
    using BM.Tools.WebControls.MForm.Controls.Helper;
    using BM.Tools.WebControls.MForm.Controls.Validators;
    using BM.Tools.WebControls.MForm.Generator;
    using BM.Tools.WebControls.MForm.ScriptReferences;

    using Navigation;

    /// <summary>
    /// The basic item control, from which all item objects inherit.
    /// </summary>
    [DesignTimeVisible(false)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Additions.Addition.js",
            LoadOrder = 0)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Additions.Constraint.js",
            LoadOrder = 1)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Additions.Restriction.js",
            LoadOrder = 2)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Additions.Descriptor.js",
            LoadOrder = 3)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Items.MItem.js",
            LoadOrder = 4)]
    [ClientScriptResource("BM.Tools.WebControls.MForm", "BM.Tools.WebControls.MForm.Controls.Items.ChoiceItem.js",
            LoadOrder = 5)]
    [ViewStateModeById]
    public abstract class OffspringItem : WebControl, IDataItemContainer, IXmlEditor
    {       
        private const string ItemCssClass = "MForm-El";        

        private ButtonContainer buttonContainer;

        private CssClasses cssClasses;

        private string displayName;

        private int index;

        /// <summary>
        /// Initializes a new instance of the <see cref="OffspringItem"/> class.
        /// </summary>
        /// <param name="basis">
        /// The basis.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        protected OffspringItem(Offspring basis, int index)
        {
            Additions = new AdditionsDataControl(basis.Additions);
            Basis = basis;
            Index = index;
            foreach (string key in basis.Style.Keys)
            {
                Style[key] = basis.Style[key];
            }

            CssClasses.Add(Basis.CssClass);
        }

        /// <summary>
        /// The event that is fired after databind.
        /// </summary>
        public event EventHandler DataBound;

        /// <summary>
        /// Invoked when the item's mode is changed
        /// </summary>
        public event EventHandler ModeChange;

        /// <summary>
        /// Gets the container control of Addition controls.
        /// </summary>
        public AdditionsDataControl Additions
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets a control from which definition the item was created.
        /// </summary>
        public Offspring Basis
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets ButtonContainer control.
        /// </summary>
        public ButtonContainer ButtonContainer
        {
            get
            {
                if (buttonContainer == null)
                {
                    buttonContainer = new ButtonContainer(this);
                }

                return buttonContainer;
            }
        }

        /// <summary>
        /// Gets CssClasses.
        /// </summary>
        public CssClasses CssClasses
        {
            get
            {
                if (cssClasses == null)
                {
                    cssClasses = new CssClasses(this, InitalizationCssClasses());
                }

                return cssClasses;
            }
        }

        /// <summary>
        /// Gets DataItem.
        /// </summary>
        public virtual object DataItem
        {
            get
            {
                return Navigable;
            }
        }

        /// <summary>
        /// Gets DataItemIndex.
        /// </summary>
        public int DataItemIndex
        {
            get
            {
                return Index;
            }
        }

        /// <summary>
        /// Gets DisplayIndex.
        /// </summary>
        public int DisplayIndex
        {
            get
            {
                return Index;
            }
        }

        /// <summary>
        /// Gets or sets DisplayName.
        /// </summary>
        public virtual string DisplayName
        {
            get
            {
                if (!string.IsNullOrEmpty(displayName))
                {
                    return displayName;
                }

                return Basis.DisplayName;
            }

            set
            {
                displayName = value;
            }
        }

        /// <summary>
        /// Gets item index.
        /// </summary>
        public int Index
        {
            get
            {
                return index;
            }

            internal set
            {
                index = value;
                ID = index.ToString(CultureInfo.InvariantCulture);                
            }
        }

        /// <summary>
        /// Gets a value indicating whether the offspring item is a prototype item.
        /// A prototype item is rendered in html as a template for items added on client side.
        /// </summary>
        public bool IsPrototypeItem
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets LocalName.
        /// </summary>
        public string LocalName
        {
            get
            {
                return Basis.LocalName;
            }
        }

        /// <summary>
        /// Gets or sets the display mode of the item
        /// if set, returns its own mode, otherwise returns its parent mode;
        /// </summary>
        public virtual FormBuilderMode Mode
        {
            get
            {
                return ViewState.GetValue("mode", Basis.Mode);                  
            }

            set
            {
                ViewState["mode"] = value;                
                if (ModeChange != null)
                {
                    ModeChange(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// Gets the string representation of the qualified name.
        /// </summary>
        public string Name
        {
            get
            {
                return Basis.Name;
            }
        }

        /// <summary>
        /// Gets or sets Navigable to which the item is databound.
        /// </summary>
        public virtual IXPathNavigable Navigable
        {
            get;
            set;
        }

        /// <summary>
        /// Gets Ordinal.
        /// </summary>
        public int Ordinal
        {
            get
            {
                return Basis.Ordinal;
            }
        }

        /// <summary>
        /// Gets the Parent Item of the item.
        /// </summary>
        public OffspringsOwnerItem ParentItem
        {
            get
            {
                return Basis.ParentItem;
            }
        }

        /// <summary>
        /// Gets Prefix of the qulaified name.
        /// </summary>
        public string Prefix
        {
            get
            {
                return Basis.Prefix;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether item removal is disabled.
        /// </summary>
        public bool RemovalDisabled
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether xml editing is enabled.
        /// </summary>
        [DefaultValue(true)]
        public bool XmlEditEnabled
        {
            get
            {
                return ViewState.GetValue("xmlEditEnabled", Basis.XmlEditEnabled);                                
            }

            set
            {
                ViewState["xmlEditEnabled"] = value;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the item should be validated.
        /// </summary>
        protected virtual bool HasReasonsNotToBeValidated
        {
            get
            {
                return !Visible || this.IsPrototypeDescendant();
            }
        }

        /// <summary>
        /// Gets TagKey.
        /// </summary>
        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                return Basis.ItemsTagKey;
            }
        }

        /// <summary>
        /// Appends the resulting xml node of this offspring to the given parent node.
        /// </summary>
        /// <param name="parent">
        /// The parent node. 
        /// </param>
        public void AppendToParent(IXPathNavigable parent)
        {
            if (IsPrototypeItem)
            {
                return;
            }

            AppendXmlToParent(parent);
        }

        /// <summary>
        /// binds the DataSource with the item
        /// </summary>
        public override void DataBind()
        {
            base.DataBind();
            if (DataBound != null)
            {
                DataBound(this, EventArgs.Empty);
            }                        
        }       

        /// <summary>
        /// checks validity of the item against its all constraints
        /// </summary>
        /// <param name="validator">
        /// The validator.
        /// </param>
        /// <returns>
        /// is the item valid
        /// </returns>
        public bool Validate(RootValidator validator)
        {
            if (HasReasonsNotToBeValidated)
            {
                return true;
            }            

            return ValidateObligatory(validator);
        }

        /// <summary>
        /// the method is fired after item is created on postback
        /// </summary>
        internal virtual void OnPostBack()
        {
        }

        internal void SetPrototypeItem()
        {
            IsPrototypeItem = true;
        }

        /// <summary>
        /// adds attributes
        /// </summary>
        /// <param name="writer">
        /// The writer.
        /// </param>
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            base.AddAttributesToRender(writer);
            writer.AddAttribute(HtmlTextWriterAttribute.Name, DisplayName);
            if (Mode != FormBuilderMode.Edit)
            {
                writer.AddAttribute("mode", Mode.ToString());
            }
        }

        /// <summary>
        /// Appends the associated xml to the specified parent node
        /// </summary>
        /// <param name="parent">
        /// The parent.
        /// </param>
        protected abstract void AppendXmlToParent(IXPathNavigable parent);        

        /// <summary>
        /// creates inner controls along with the button container
        /// </summary>
        protected override void CreateChildControls()
        {            
            Controls.AddAt(0, ButtonContainer);
            base.CreateChildControls();
        }

        /// <summary>
        /// returns the css classes that are ascribed to this item
        /// </summary>
        /// <returns>
        /// the css classes that are ascribed to this item
        /// </returns>
        protected virtual string[] InitalizationCssClasses()
        {
            return new[]
                   {
                           ItemCssClass
                   };
        }

        /// <summary>
        /// The additions container control must be added on init to support post back validation.
        /// </summary>
        /// <param name="e">
        /// The event args.
        /// </param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            Controls.Add(Additions);
        }

        /// <summary>
        /// includes client script resources
        /// </summary>
        /// <param name="e">
        /// The event args
        /// </param>
        protected override void OnPreRender(EventArgs e)
        {
            ScriptIncluder.IncludeResources(this);
            base.OnPreRender(e);            
        }

        /// <summary>
        /// checks validity of the item against its all constraints if it has to be validated
        /// </summary>
        /// <param name="validator">
        /// The validator.
        /// </param>
        /// <returns>
        /// is the item valid
        /// </returns>
        protected virtual bool ValidateObligatory(RootValidator validator)
        {
            foreach (Constraint constraint in Additions.Constraints.Where(constraint => constraint.Enabled))
            {
                if (!constraint.IsValid(this))
                {
                    validator.ErrorCauses.Add(new FailedConstraintErrorCause(this, constraint));
                    return false;
                }
            }

            return true;
        }        
    }
}